﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace DefensiveShield
{
    public static partial class OS
    {
        //---------------------------------------------------------------------------------------------------------------------------------
        //---------------------------------------------------------------------------------------------------- CHECK USER PASSWORD --------
        //---------------------------------------------------------------------------------------------------------------------------------
        /**
            @brief Функция проверки пароля польхователя
             * 
             * Данная функция проверяет является ли пароль пользователя верным
             * 
            @return ErrorCodes.NoError - если пароль верен
            @return код ошибки - иначе
        */
        public static ErrorCodes CheckUserPassword(string userName, string passwd)
        {
            try
            {
                if (Users[userName].PasswdHash == Hash(passwd))
                    return ErrorCodes.NoError;
                else
                {
                    LogMessage(userName, "Wrong password.");
                    return ErrorCodes.WrongPassword;
                }
            }
            catch (Exception except)
            {
                LogMessage(userName, "Error checking user password. Exception: " + except.Message);
                return ErrorCodes.UnknownErrorInCheckUserPassword;
            }
        }

        //---------------------------------------------------------------------------------------------------------------------------------
        //---------------------------------------------------------------------------------------------------- SET ACCESS PERMISSION ------
        //---------------------------------------------------------------------------------------------------------------------------------
        /**
            @brief Функция добавления прав дискреционного доступа
            @param minimalSecrecy - уровень секретности текущей сессии пользователя
             * 
             * Данная функция назначает исключительные дискреционные права доступа для конкретного пользователя к конкретному файлу, которые будут соблюдаться вопреки групповым политикам
             * 
            @return код ошибки, при её наличии
        */
        public static ErrorCodes SetAccessPermissions (OsSession session, Rights rights, string fileName)
        {
            if (!Files.ContainsKey(fileName))
            {
                LogMessage(session.UserName, "Error in exclusive right to file, because this file is not registered, register it firstly", fileName);
                return ErrorCodes.OpenAttemptOfUnregisteredFile;
            }
            if (Files[fileName].ExclusiveUserAccessRights.ContainsKey(session.UserName))
                Files[fileName].ExclusiveUserAccessRights[session.UserName] = rights;
            else
                Files[fileName].ExclusiveUserAccessRights.Add(session.UserName, rights);
            
            LogMessage(session.UserName, "Successful setting exclusive rights for user.", fileName);
            return ErrorCodes.NoError;
        }

        //---------------------------------------------------------------------------------------------------------------------------------
        //---------------------------------------------------------------------------------------------------- CHECK MANDATORY ACCESS -----
        //---------------------------------------------------------------------------------------------------------------------------------
        /**
            @brief Функция проверки мандатного доступа к объекту файловой системы
             * 
             * Данная функция проверяет доступ пользователя к объекту файловой системы, проверяя все права рекурсивно от корня файловой системы, до самого файла, если какой-либо объект на этом пути будет являться не зарегистрированным, то можно считать, что доступ к нему открыт.
             * 
            @return ErrorCodes.NoError - если пользователь имеет доступ к файлу, и код ошибки в случае ошибки
        */
        public static ErrorCodes CheckMandatoryAccessPermission(OsSession session, string fileName)
        {
            try
            {
                // Admin is only admin - not a superuser
                if (session.UserName == AdministratorName)
                    return ErrorCodes.NoError;

                var secrecy = Secrecy.NonSecret;
                var currentDirInfo = new DirectoryInfo(fileName);

                while (currentDirInfo != null)
                {
                    var currentPath = currentDirInfo.FullName;
                    //Check if file is registered (is it required?) and update secrecy level if needed
                    if (Files.ContainsKey(currentPath))
                        if (secrecy.CompareTo(Files[currentPath].SecrecyNum) < 0)
                            secrecy = Files[currentPath].SecrecyNum;
                    //Move to the parent directory
                    currentDirInfo = currentDirInfo.Parent;
                }

                if (secrecy.CompareTo(Users[session.UserName].SecrecyNum) <= 0)
                    return ErrorCodes.NoError;
                else
                    return ErrorCodes.MandatoryAccessDenied;
            }
            catch (Exception except)
            {
                LogMessage(session.UserName, "Error in checking mandatory access permissions. Exception: " + except.Message, fileName);
                return ErrorCodes.UnknownError;
            }
        }

        //---------------------------------------------------------------------------------------------------------------------------------
        //---------------------------------------------------------------------------------------------------- CHECK DISCRETIONAL ACCESS --
        //---------------------------------------------------------------------------------------------------------------------------------
        /**
            @brief Функция проверки дискреционного доступа к объекту файловой системы
             * 
             * Данная функция проверяет доступ пользователя к объекту файловой системы, проверяя все права рекурсивно от корня файловой системы, до самого файла, если какой-либо объект на этом пути будет являться не зарегистрированным, то можно считать, что доступ к нему открыт.
             * В качестве возвращаемого значения выдаётся минимальный доступ среди всей цепочки вложенности.
             * Администратор имеет доступ ко всем файлам
            @return Rights - тип разрешённого доступа
        */
        public static Rights GetDiscretionalAccessPermission (OsSession session, string fileName)
        {
            try
            {
                // Admin is only admin - not a superuser
                if (session.UserName == AdministratorName)
                    return Rights.ReadWrite;

                var rights = Rights.ReadWrite;
                var currentDirInfo = new DirectoryInfo(fileName);

                while (currentDirInfo != null)
                {
                    var currentPath = currentDirInfo.FullName;

                    if (Files.ContainsKey(currentPath))
                    {
                        var file = Files[currentPath];
                        //First of all check exclusive rights
                        if (file.ExclusiveUserAccessRights.ContainsKey(session.UserName))
                        {
                            if (file.ExclusiveUserAccessRights[session.UserName].CompareTo(rights) < 0)
                                rights = file.ExclusiveUserAccessRights[session.UserName];
                        }
                            //Check if owner
                        else if (file.OwnerName == session.UserName)
                        {
                            //Supposing that owner has ReadWrite rights
                            if (Rights.ReadWrite.CompareTo(rights) < 0)
                                rights = Rights.ReadWrite;
                        }
                            //Check if in Group
                        else if (file.GroupName == Users[session.UserName].GroupName)
                        {
                            if (file.GroupRights.CompareTo(rights) < 0)
                                rights = file.GroupRights;
                        }
                            //Others
                        else
                        {
                            if (file.OtherRights.CompareTo(rights) < 0)
                                rights = file.OtherRights;
                        }
                    }
                    //Move to the parent directory
                    currentDirInfo = currentDirInfo.Parent;
                }

                return rights;
            }
            catch (Exception except)
            {
                LogMessage(session.UserName, "Error in checking discretional access permission. Exception: " + except.Message, fileName);
                return Rights.Noright;
            }
        }
    }
}
